

/**
 * 全局事件通知
 */

class Notifier {

    private $map: Map<NotifyType, IObservable[]>;

    constructor($map?: Map<NotifyType, IObservable[]>) {
        if ($map === void 0) {
            $map = new Map();
        }
        this.$map = $map;
    }

    /**
     * 注册事件
     * @param type 
     * @param notified 
     * @param observer 
     * @param priority 
     * @returns 
     */
    public attach(type: NotifyType, notified: NotifyHandler, observer?: any, priority?: number): boolean {
        if (priority === void 0) { priority = 0; }
        if (typeof (notified) != 'function') {
            console.warn('attacher must be function.');
            return false;
        }
        priority = Number.isFinite(priority) ? priority : 0;

        let obs = this.$map.get(type);
        if (!Array.isArray(obs)) {
            this.$map.set(type, [{ notified: notified, observer: observer, priority: priority }]);
            return true;
        }
        for (const item of obs) {
            if (item.notified === notified && item.observer === observer) {
                console.warn('duplicated attached.');
                return false;
            }
        }

        let pos = 0;
        for (let i = 0; i < obs.length; i++) {
            let ob = obs[i];
            if (ob.priority < priority) {
                pos = i;
                break;
            }
            pos++;
        }
        obs.splice(pos, 0, { notified: notified, observer: observer, priority: priority });
        return true;
    }

    /**
     * 注销事件
     * @param type 
     * @param notified 
     * @param observer 
     * @returns 
     */
    public detach(type: NotifyType, notified: NotifyHandler, observer?: any): void {
        if(typeof(notified) != 'function') return;
        let obs = this.$map.get(type);
        if(this._notEmptyArr(obs)) {
            for (let i = 0; i < obs.length; i++) {
                let ob = obs[i];
                let comp1 = !!observer && observer === ob.observer && notified === ob.notified;
                let comp2 = !observer && notified === ob.notified;
                if(comp1 || comp2) {
                    obs.slice(i, 1);
                    i--;
                }
                if(obs.length == 0) {
                    this.$map.delete(type);
                }
            }
        }
    }

    /**
     * 注销事件 observer 所有事件
     * @param observer 
     * @returns 
     */
    public unload(observer: any): void {
        if(!observer) return;
        for (const key of this.$map.keys()) {
            let item = this.$map.get(key);
            if(!this._notEmptyArr(item)){
                continue;
            }

            for (let i = 0; i < item.length; i++) {
                if(item[i].observer === observer) {
                    item.splice(i, 1);
                    i--;
                } 
                if(item.length == 0) {
                    this.$map.delete(key);
                }
            }
        }
    }

    /** 派发事件 */
    public dispatch(type: NotifyType, ...data: any[]): void {
        var obs = this.$map.get(type);
        if(!this._notEmptyArr(obs)) return;

        for (let i = 0; i < obs.length; i++) {
            let item = obs[i];
            item.notified.apply(item.observer, data);
        }
    }


    public clear(type: NotifyType): void {
        this.$map.delete(type);
    }
    public clearAll(): void {
        this.$map.clear();
    }
    private _notEmptyArr(array: any): boolean {
        return Array.isArray(Array) && array.length > 0;
    }
}

export default new Notifier();